package instrumentation;

import java.util.HashMap;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.logging.Logger;
import java.util.LinkedList;
import java.util.ArrayList;
import soot.ArrayType;
import java.util.SortedMap;
import java.util.TreeMap;
import java.util.HashSet;
import java.util.Iterator;
import soot.Body;
import soot.BooleanType;
import soot.ByteType;
import soot.CharType;
import soot.DoubleType;
import soot.FloatType;
import soot.IntType;
import soot.IntegerType;
import soot.Local;
import soot.LongType;
import soot.SootMethod;
import soot.NullType;
import soot.PrimType;
import soot.RefLikeType;
import soot.RefType;
import soot.Scene;
import soot.SootClass;
import soot.Type;
import soot.Unit;
import soot.jimple.CmpExpr;
import soot.jimple.CmpgExpr;
import soot.jimple.CmplExpr;
import soot.jimple.ArrayRef;
import soot.jimple.internal.JArrayRef;
import soot.jimple.AssignStmt;
import soot.jimple.CastExpr;
import soot.jimple.ReturnStmt;
import soot.jimple.ReturnVoidStmt;
import soot.jimple.ConditionExpr;
import soot.jimple.DefinitionStmt;
import soot.jimple.DoubleConstant;
import soot.jimple.EqExpr;
import soot.jimple.Expr;
import soot.jimple.FieldRef;
import soot.jimple.FloatConstant;
import soot.jimple.GeExpr;
import soot.jimple.GtExpr;
import soot.jimple.IdentityStmt;
import soot.jimple.GotoStmt;
import soot.jimple.IfStmt;
import soot.jimple.RetStmt;
import soot.jimple.InstanceFieldRef;
import soot.jimple.IntConstant;
import soot.jimple.InvokeExpr;
import soot.jimple.Jimple;
import soot.jimple.LeExpr;
import soot.jimple.LongConstant;
import soot.jimple.LookupSwitchStmt;
import soot.jimple.LtExpr;
import soot.jimple.NeExpr;
import soot.jimple.NewArrayExpr;
import soot.jimple.NewMultiArrayExpr;
import soot.jimple.NullConstant;
import soot.jimple.InvokeStmt;
import soot.jimple.StaticInvokeExpr;
import soot.jimple.SpecialInvokeExpr;
import soot.jimple.VirtualInvokeExpr;
import soot.toolkits.scalar.UnitValueBoxPair;
import soot.jimple.Stmt;
import soot.jimple.StringConstant;
import soot.jimple.TableSwitchStmt;
import soot.toolkits.graph.ExceptionalUnitGraph;
import soot.toolkits.graph.UnitGraph;
import soot.toolkits.scalar.CombinedAnalysis;
import soot.toolkits.scalar.CombinedDUAnalysis;
import soot.jimple.internal.JSpecialInvokeExpr;
import soot.jimple.internal.JLengthExpr;
import soot.jimple.internal.JInstanceFieldRef;
import soot.jimple.internal.JVirtualInvokeExpr;
import soot.util.Chain;
import util.GAConfig;
import soot.jimple.ThrowStmt;

import instrumentation.Instrumenter.UnifiedInstrumentator;
import instrumentation.ConditionIf.ConditionType;
public class CopyOfWhiteInstrumenter implements UnifiedInstrumentator{
	private static final Logger logger = Logger.getLogger("WhiteInstrumenter");

	private static final SootClass trackerClass;
	private static SootMethod trackerSingleton;
	private static SootMethod getConditionTargetId;
	private static final SootMethod calculateConditionTargetDistance;
	private static final SootMethod setConditionTargetDistance;

	private static final SootMethod branchHit;
	
	private static final SootMethod branchCovered;
	/** list of  uses for the fields  */
	private Set<DataUse> uses = new HashSet<DataUse>();


	static {

		final String TRACKER = TrackerCoverage.class.getName();
		Scene.v().loadClassAndSupport(TRACKER);
		trackerClass = Scene.v().getSootClass(TRACKER);
		trackerSingleton = trackerClass.getMethodByName("getTracker");
		
		getConditionTargetId = trackerClass.getMethodByName("getConditionTargetId");
		calculateConditionTargetDistance = trackerClass.getMethodByName("calculateConditionTargetDistance");
		setConditionTargetDistance = trackerClass.getMethodByName("setConditionTargetDistance");

		branchHit = trackerClass.getMethodByName("branchHit");
		branchCovered = trackerClass.getMethodByName("branchCovered");

	}
	public CopyOfWhiteInstrumenter() {
		System.out.println("instrumenter loaded");
	}
	private Analyzer analyzer;
	private Local localTracker;
	private Local localConditionTarget;
	private Local localTmpDouble1;
	private Local localTmpDouble2;
	private String methodName;
	private static int branchId=1;
	private static int switchCaseNoGenerator=1;

	private int falseBranch;
	private int trueBranch;
	Condition.DataType condDataType;
	private Type typeOp1;
	private SootMethod method;
	private String extraInfo=null;
	private List<DataDef> defs=new LinkedList<DataDef>();
	private List<Data> readOnlyParams=new LinkedList<Data>();
	private List <Value> staticInvokeArgs=new LinkedList<Value>();

	@Override
	public int init(Body oldBody, Body newBody, Chain<Unit> newUnits, IdentityStmt[] paramDefs) {
		// some useful constants
		final SootMethod method = newBody.getMethod();
		final SootClass sClass = method.getDeclaringClass();
		final BlockClass clazz = Factory.singleton.get(sClass, Factory.singleton.get(sClass.getFields()));
//		final ConditionClass clazz = Factory.singleton.get(sClass, Factory.singleton.get(sClass.getFields()));

		localTracker = Jimple.v().newLocal("__tracker__", trackerClass.getType());
		newBody.getLocals().add(localTracker);
		newUnits.add(Jimple.v().newAssignStmt(localTracker, Jimple.v().newStaticInvokeExpr(trackerSingleton.makeRef())));

		localConditionTarget = Jimple.v().newLocal("condition_target__", IntType.v());
		newBody.getLocals().add(localConditionTarget);
	
//		newUnits.add(Jimple.v().newAssignStmt(localConditionTarget, Jimple.v().newVirtualInvokeExpr(localTracker, getConditionTargetId.makeRef())));

		localTmpDouble1 = Jimple.v().newLocal("_tmp_double_1__", DoubleType.v());
		newBody.getLocals().add(localTmpDouble1);

		localTmpDouble2 = Jimple.v().newLocal("_tmp_double_2__", DoubleType.v());
		newBody.getLocals().add(localTmpDouble2);
		

		
		analyzer = new Analyzer(newUnits, clazz, newBody, oldBody, paramDefs);
		return branchId;

	}
	@Override
	public void done() {
		for(BlockClass c : Factory.singleton.getClasses()) {
			//	c.performDataFlowAnalysis();
				c.write(GAConfig.getDirInstrumented(), c.getClzName());
		}
	}
	@Override
	public void processPre(Chain<Unit> newUnits, Stmt op) {
		analyzer.process(newUnits, op);
	}
	@Override
	public int processPost(Chain<Unit> newUnits, Stmt op) {
		analyzer.processPost(newUnits, op);
		return branchId;
	}
	class Analyzer {
		private final CombinedAnalysis duAnalysis;
		private final Map<Local, Data> localRepository;
		private final Map<Unit, Set<Integer>> toLinkMap;
		private  BlockClass clazz;
		private final BlockMethodEntry start;
		private  BlockMethodCall methodCall;
		final boolean methodPrivate;
		/** link the first unit of each building block to the set of incoming edges (to complete) */
		public Analyzer(Chain<Unit> newUnits,BlockClass clazz1, Body newBody, Body oldBody, IdentityStmt[] paramDefs) {
			 clazz=clazz1;
			 method = newBody.getMethod();
			 methodName = method.getSubSignature();
			 
			 System.out.println("methodName: " + methodName.toString()); 
			 methodPrivate = method.isPrivate();
			final UnitGraph unitGraph = new ExceptionalUnitGraph(oldBody);
			duAnalysis = CombinedDUAnalysis.v(unitGraph);
			toLinkMap = new HashMap<Unit, Set<Integer>>();
			localRepository = new HashMap<Local, Data>();
			for(int i = 0; i < paramDefs.length; i++) {
				final Local localParam = newBody.getParameterLocal(i);
				Data dataParam = new Data(null,localParam.getType().toString(),null,i, true);
				readOnlyParams.add(dataParam);
				localRepository.put(localParam, dataParam);
			}
			start = new BlockMethodEntry(clazz, methodName, method.isPrivate());
			clazz.addMethod(start);
			methodCall=null;
			extraInfo=null;
		}
		private void preProcess(Chain<Unit> newUnits, Stmt stmt) {
			 DataDef dataDef=null;
			if(stmt.containsInvokeExpr()){
				InvokeExpr expr = stmt.getInvokeExpr();
				soot.SootClass cls=expr.getMethod().getDeclaringClass();
				if(cls.getName().startsWith("java.")&&!cls.getName().equals("java.lang.String")) return;
				if(cls.getSuperclass().getName().equals("java.lang.Exception"))return;
				if(!cls.getName().equals("java.lang.String"))
						methodCall=new BlockMethodCall(expr.getMethod().getDeclaringClass().toString()
									,expr.getMethod().getDeclaration(),expr.getMethod().isPrivate());
			}

			if(stmt instanceof IfStmt){
				falseBranch=branchId++;
				trueBranch=branchId++;

				newUnits.add(Jimple.v().newInvokeStmt(Jimple.v().newVirtualInvokeExpr(localTracker, branchHit.makeRef(), IntConstant.v(falseBranch))));
				newUnits.add(Jimple.v().newInvokeStmt(Jimple.v().newVirtualInvokeExpr(localTracker, branchHit.makeRef(),IntConstant.v(trueBranch))));
			}
		}


		public void process(Chain<Unit> newUnits, Stmt op) {
			preProcess(newUnits, op);
	//		System.out.println(op);
			if(op instanceof AssignStmt)
				process(newUnits, (AssignStmt) op);
			else if(op instanceof GotoStmt)
				process(newUnits, (GotoStmt) op);
			else if(op instanceof IdentityStmt)
				process(newUnits, (IdentityStmt) op);
			else if(op instanceof IfStmt)
				process(newUnits, (IfStmt) op);
			else if(op instanceof LookupSwitchStmt)
				process(newUnits, (LookupSwitchStmt) op);
			else if(op instanceof TableSwitchStmt)
				process(newUnits, (TableSwitchStmt) op);
			else if(op instanceof RetStmt)
				process(newUnits, (RetStmt) op);
			else if(op instanceof ReturnVoidStmt)
				process(newUnits, (ReturnVoidStmt) op);
			else if(op instanceof ReturnStmt)
				process(newUnits, (ReturnStmt) op);
			else if(op instanceof ThrowStmt)
				process(newUnits, (ThrowStmt) op);
			else if(op instanceof InvokeStmt)
				process(newUnits, (InvokeStmt) op);


			else {
				//logger.info("WhiteInstrumenter.process cannot analyze " + op + " (" + op.getClass().getName() + ")");
			}
		}
		public void process(Chain<Unit> newUnits, InvokeStmt u) {
			if(!(u.getInvokeExpr() instanceof SpecialInvokeExpr) )return;
			SpecialInvokeExpr fr = (SpecialInvokeExpr) u.getInvokeExpr();
			Data base=getData(getRealUse(fr.getBase(), u, false));
			//Value v=getValue(getRealUse(leftOp, stmt, false));
			List <Value> args=new LinkedList<Value>();
			if(base==null){//(if "--".equal(str))
				if(getValue(getRealUse(fr.getBase(), u, false))instanceof ConstantString){
					args.add(getValue(getRealUse(fr.getBase(), u, false)));
					if(fr.getArgs().size()==1)
						base=getData(getRealUse(fr.getArg(0), u, false));
				}
			}
			else{
					for(int i=0; i<fr.getArgs().size();i++){
						Value arg=getValue(getRealUse(fr.getArg(i),u,false));
							args.add(arg);
					}
			}

		}

		public void process(Chain<Unit> newUnits, GotoStmt u) {
			methodCall = null;
			extraInfo=null;
		}

		public void process(Chain<Unit> newUnits, RetStmt u) {
			methodCall = null;
			defs.clear();
			extraInfo=null;
		}
		public void process(Chain<Unit> newUnits, ReturnVoidStmt u) {
			defs.clear();
			methodCall = null;
			extraInfo=null;
		}
		public void process(Chain<Unit> newUnits, ThrowStmt u) {
			methodCall = null;
			defs.clear();
			extraInfo=null;
		}

		public void processFieldRef(Chain<Unit> newUnits, Stmt op){
			System.out.print(op.getFieldRef().getField());
			
		}
		private DataUse handleUse(Chain<Unit> newUnits, soot.Value v, Stmt u) {
			v = getRealUse(v, u, false);
			if(v == null) return null;
			Data data = getData(v);
			if(data == null) return null;
			DataUse use = new DataUse(data, defs);
			uses.add(use);
			return use;

		}
		private void add(int branchId, Unit stmt){
			Set<Integer> set = toLinkMap.get(stmt);
            if(set == null) {
                    set = new HashSet<Integer>();
                    toLinkMap.put(stmt, set);
            }

            set.add(branchId);
		}
		public void process(Chain<Unit> newUnits, IfStmt u) {
			ConditionExpr expr = (ConditionExpr) u.getCondition();
			
			soot.Value op1 = expr.getOp1();
			soot.Value op2 = expr.getOp2();
			typeOp1 = op1.getType();
	         condDataType = getType(op1);

	         Stmt useStmt = u;
				// check if this is a comparison between floats or doubles
				if(typeOp1 instanceof ByteType && op1 instanceof Local && op2 instanceof IntConstant && ((IntConstant)op2).value == 0) {
					List<Unit> defs = duAnalysis.getDefsOfAt((Local) op1, u);

					if(defs.size() == 1) {
						Stmt def = (Stmt) defs.get(0);

						if (def instanceof AssignStmt) {
							soot.Value rightOp = ((AssignStmt)def).getRightOp();

							if (rightOp instanceof CmplExpr) {
								op1 = ((CmplExpr)rightOp).getOp1();
								op2 = ((CmplExpr)rightOp).getOp2();
								typeOp1 = op1.getType();
								useStmt = def;

								logger.fine("The conditon " + u + " has been recognized as a comparison between " + op1 + " and " + op2);
							} else if (rightOp instanceof CmpExpr) {
								op1 = ((CmpExpr)rightOp).getOp1();
								op2 = ((CmpExpr)rightOp).getOp2();
								typeOp1 = op1.getType();
								useStmt = def;

								logger.fine("The conditon " + u + " has been recognized as a comparison between " + op1 + " and " + op2);
							} else if (rightOp instanceof CmpgExpr) {
								op1 = ((CmpgExpr)rightOp).getOp1();
								op2 = ((CmpgExpr)rightOp).getOp2();
								typeOp1 = op1.getType();
								useStmt = def;

								logger.fine("The conditon " + u + " has been recognized as a comparison between " + op1 + " and " + op2);
							}

						}
					}
				}

         System.out.println("trueBranch="+ trueBranch);
    	 System.out.println("readOnlyParams : "+ readOnlyParams.toString());
		 Value v1 = getValue(getRealUse(op1, useStmt, false));
		 Value v2 = getValue(getRealUse(op2, useStmt, false));
//		 if(v1 instanceof Constant&&)
		 System.out.println("defs List: "+ defs.toString());
		 final DataUse use1 = handleUse(newUnits, op1, useStmt);
		 final DataUse use2 = handleUse(newUnits, op2, useStmt);
		 if(use1!=null)
			 System.out.println("use1 : "+ use1.toString());
		 if(use2!=null)
			 System.out.println("use2 : "+ use2.toString());
		 else{
			 System.out.println("use2 : "+staticInvokeArgs);
			 for(Value value:staticInvokeArgs)
				 if(value instanceof ConstantString)
					 v2=value;
		 }
			ConditionIf.ConditionType condType = null;
			if(expr instanceof LtExpr) condType = ConditionType.LT;
			if(expr instanceof LeExpr) condType = ConditionType.LE;
			if(expr instanceof NeExpr) condType = ConditionType.NE;
			if(expr instanceof EqExpr) condType = ConditionType.EQ;
			if(expr instanceof GeExpr) condType = ConditionType.GE;
			if(expr instanceof GtExpr) condType = ConditionType.GT;
				
			

			ConditionIf tCondition = new ConditionIf(trueBranch, expr.toString(), 
			condDataType,v1,use1, condType, v2,use2,  methodCall);

			start.addCondition(tCondition);
			ConditionIf fCondition = new ConditionIf(falseBranch, expr.toString(), 
					condDataType,v1,use1, condType, v2,use2,  methodCall);
			start.addCondition(fCondition);
			add(trueBranch, u.getTarget());
	        extraInfo=null;
	        methodCall=null;
	        staticInvokeArgs.clear();
			// the expression "if(expr)" is instrumented this way:
			//  1. if(expr) goto 10
			//  2. branchCovered(false)
			//  2a trackPUse(false, usesOfExpr)  { if PUse tracking}
			//  3. if(condTarget != trueBranch) goto 7
			//  4. trackDistance(op1, op2);
			//  5. goto 17
			//  7. if(contTarget != falseBranch) goto 17
			//  8. trackDistance(0);
			//  9. goto 17

			// 10. branchCovered(true)
			// 10a trackPUse(true, usesOfExpr)  { if PUse tracking}
			// 11. if(condTarget != trueBranch) goto 14
			// 12. trackDistance(0);
			// 13. goto 17
			// 14. if(contTarget != falseBranch) goto 17
			// 15. trackDistance(op1, op2);
			// 16. goto 17
			// 17. nop (after)

			Unit after = Jimple.v().newNopStmt();

			Unit handleTrue = Jimple.v().newNopStmt();
			
			newUnits.add(Jimple.v().newIfStmt(expr, handleTrue));
			{ // handle false

				newUnits.add(Jimple.v().newInvokeStmt(Jimple.v().newVirtualInvokeExpr(localTracker, branchCovered.makeRef(), IntConstant.v(falseBranch))));
				newUnits.add(Jimple.v().newAssignStmt(localConditionTarget, Jimple.v().newVirtualInvokeExpr(localTracker, getConditionTargetId.makeRef(),IntConstant.v(trueBranch))));

				
				// calculate distance (true)
				Unit nop = Jimple.v().newNopStmt();
				newUnits.add(Jimple.v().newIfStmt(Jimple.v().newNeExpr(localConditionTarget, IntConstant.v(trueBranch)), nop));
				if(typeOp1 instanceof BooleanType){
					newUnits.add(Jimple.v().newInvokeStmt(Jimple.v().newVirtualInvokeExpr(localTracker, setConditionTargetDistance.makeRef(), DoubleConstant.v(1))));

				}//if(type instanceof BooleanType){
				else if(typeOp1 instanceof IntegerType || typeOp1 instanceof LongType || typeOp1 instanceof FloatType || typeOp1 instanceof DoubleType) {
					newUnits.add(Jimple.v().newAssignStmt(localTmpDouble1,
							(op1.getType() instanceof DoubleType) ? op1 : Jimple.v().newCastExpr(op1, DoubleType.v())));

					newUnits.add(Jimple.v().newAssignStmt(localTmpDouble2,
							(op2.getType() instanceof DoubleType) ? op2 : Jimple.v().newCastExpr(op2, DoubleType.v())));

					newUnits.add(Jimple.v().newInvokeStmt(Jimple.v().newVirtualInvokeExpr(localTracker, calculateConditionTargetDistance.makeRef(), Arrays.asList(localTmpDouble1, localTmpDouble2))));
				} else if(typeOp1 instanceof RefLikeType) {

					// localTmpDouble1 = (op1 == null) ? 0 : 1;
					Unit isNull = Jimple.v().newNopStmt();
					newUnits.add(Jimple.v().newAssignStmt(localTmpDouble1, DoubleConstant.v(0)));
					newUnits.add(Jimple.v().newIfStmt(Jimple.v().newEqExpr(op1, NullConstant.v()), isNull));
					newUnits.add(Jimple.v().newAssignStmt(localTmpDouble1, DoubleConstant.v(0)));
					newUnits.add(isNull);

					// localTmpDouble2 = (op2 == null) ? 0 : 1;
					isNull = Jimple.v().newNopStmt();
					newUnits.add(Jimple.v().newAssignStmt(localTmpDouble2, DoubleConstant.v(0)));
					newUnits.add(Jimple.v().newIfStmt(Jimple.v().newEqExpr(op2, NullConstant.v()), isNull));
					newUnits.add(Jimple.v().newAssignStmt(localTmpDouble2, DoubleConstant.v(0)));
					newUnits.add(isNull);
					newUnits.add(Jimple.v().newInvokeStmt(Jimple.v().newVirtualInvokeExpr(localTracker, calculateConditionTargetDistance.makeRef(), Arrays.asList(localTmpDouble1, localTmpDouble2))));

				}
				newUnits.add(Jimple.v().newAssignStmt(localConditionTarget, Jimple.v().newVirtualInvokeExpr(localTracker, getConditionTargetId.makeRef(),IntConstant.v(falseBranch))));
				newUnits.add(Jimple.v().newGotoStmt(after));
				// calculate distance (false)
				newUnits.add(nop);
				newUnits.add(Jimple.v().newIfStmt(Jimple.v().newNeExpr(localConditionTarget, IntConstant.v(falseBranch)), after));
				newUnits.add(Jimple.v().newGotoStmt(after));
			}
			newUnits.add(handleTrue);
			{ // handle true
				// 10. branchCovered(true)
				// 10a trackPUse(true, usesOfExpr)  { if PUse tracking}
				// 11. if(condTarget != trueBranch) goto 14
				// 12. trackDistance(0);
				// 13. goto 17
				// 14. if(contTarget != falseBranch) goto 17
				// 15. trackDistance(op1, op2);
				// 16. goto 17
				// 17. nop (after)

				newUnits.add(Jimple.v().newInvokeStmt(Jimple.v().newVirtualInvokeExpr(localTracker, branchCovered.makeRef(), IntConstant.v(trueBranch))));
				newUnits.add(Jimple.v().newAssignStmt(localConditionTarget, Jimple.v().newVirtualInvokeExpr(localTracker, getConditionTargetId.makeRef(),IntConstant.v(falseBranch))));
				// calculate distance (true)
				Unit nop = Jimple.v().newNopStmt();
				newUnits.add(Jimple.v().newIfStmt(Jimple.v().newNeExpr(localConditionTarget, IntConstant.v(trueBranch)), nop));
		
				newUnits.add(Jimple.v().newGotoStmt(after));

				// calculate distance (false)
				newUnits.add(nop);
				newUnits.add(Jimple.v().newIfStmt(Jimple.v().newNeExpr(localConditionTarget, IntConstant.v(falseBranch)), after));

				if(typeOp1 instanceof BooleanType){
					newUnits.add(Jimple.v().newInvokeStmt(Jimple.v().newVirtualInvokeExpr(localTracker, setConditionTargetDistance.makeRef(), DoubleConstant.v(1))));
				}	
				else if(typeOp1 instanceof IntegerType || typeOp1 instanceof LongType || typeOp1 instanceof FloatType || typeOp1 instanceof DoubleType) {
					newUnits.add(Jimple.v().newAssignStmt(localTmpDouble1,
							(op1.getType() instanceof DoubleType) ? op1 : Jimple.v().newCastExpr(op1, DoubleType.v())));

					newUnits.add(Jimple.v().newAssignStmt(localTmpDouble2,
							(op2.getType() instanceof DoubleType) ? op2 : Jimple.v().newCastExpr(op2, DoubleType.v())));

					newUnits.add(Jimple.v().newInvokeStmt(Jimple.v().newVirtualInvokeExpr(localTracker, calculateConditionTargetDistance.makeRef(), Arrays.asList(localTmpDouble1, localTmpDouble2))));

				} else if(typeOp1 instanceof RefLikeType) {

					// localTmpDouble1 = (op1 == null) ? 0 : 1;
					Unit isNull = Jimple.v().newNopStmt();
					newUnits.add(Jimple.v().newAssignStmt(localTmpDouble1, DoubleConstant.v(0)));
					newUnits.add(Jimple.v().newIfStmt(Jimple.v().newEqExpr(op1, NullConstant.v()), isNull));
					newUnits.add(Jimple.v().newAssignStmt(localTmpDouble1, DoubleConstant.v(0)));
					newUnits.add(isNull);

					// localTmpDouble2 = (op2 == null) ? 0 : 1;
					isNull = Jimple.v().newNopStmt();
					newUnits.add(Jimple.v().newAssignStmt(localTmpDouble2, DoubleConstant.v(0)));
					newUnits.add(Jimple.v().newIfStmt(Jimple.v().newEqExpr(op2, NullConstant.v()), isNull));
					newUnits.add(Jimple.v().newAssignStmt(localTmpDouble2, DoubleConstant.v(0)));
					newUnits.add(isNull);
					newUnits.add(Jimple.v().newInvokeStmt(Jimple.v().newVirtualInvokeExpr(localTracker, calculateConditionTargetDistance.makeRef(), Arrays.asList(localTmpDouble1, localTmpDouble2))));

				}
				newUnits.add(Jimple.v().newGotoStmt(after));
			}

			// done
			newUnits.add(after);
	//		current = null;
		}
		public  Condition.DataType getType(soot.Value v) {

			Type type = v.getType();

			if(type instanceof BooleanType)
				return Condition.DataType.Boolean;

			if(type instanceof CharType)
				return Condition.DataType.Character;

			if(type instanceof PrimType)
				return Condition.DataType.Number;

			if(type instanceof RefType) {
				if(((RefType) type).getClassName().equals("java.lang.String"))
					return Condition.DataType.String;

				return Condition.DataType.Reference;
			}

			if(type instanceof NullType)
				return Condition.DataType.Reference;

			if(type instanceof ArrayType)
				return Condition.DataType.Array;

		//	if(TestFul.DEBUG) TestFul.debug("Unknown data type: " + type);


			return Condition.DataType.Reference;
		}
		public void process(Chain<Unit> newUnits, LookupSwitchStmt u) {
			final soot.Value key = u.getKey();
			Value v1 = getValue(getRealUse(key, u, false));
			final DataUse use = handleUse(newUnits, key, u);
			int current= switchCaseNoGenerator++;
			condDataType = getType(key);
			ConditionSwitch c = new ConditionSwitch(current,v1,condDataType,use);
			start.addCondition(c);
		
				condDataType=null;
			List<IntConstant> lookupValues = new ArrayList<IntConstant>();
			List<Unit> targets = new ArrayList<Unit>();

			int[] keys = new int[u.getTargetCount()];
			SortedMap<Integer, Unit> keyTarget = new TreeMap<Integer, Unit>();
			Map<Integer, Integer> keyBranchId = new HashMap<Integer, Integer>();
			for(int i = 0; i < u.getTargetCount(); i++) {
				final int value = u.getLookupValue(i);
				keys[i] = value;
				lookupValues.add(IntConstant.v(value));

		
				int caseNo=addBranch( u.getTarget(i));
			    keyBranchId.put(value, caseNo);
				c.addCase(caseNo,value);

				Unit target = Jimple.v().newNopStmt();
				targets.add(target);
				keyTarget.put(value, target);
				newUnits.add(Jimple.v().newInvokeStmt(Jimple.v().newVirtualInvokeExpr(localTracker, branchHit.makeRef(), IntConstant.v(caseNo))));

			}

			Unit defaultTarget = Jimple.v().newNopStmt();
			Integer defaultBranchId;
			{
				defaultBranchId=addBranch( u.getDefaultTarget());
				c.setDefaultCase(defaultBranchId);
				newUnits.add(Jimple.v().newInvokeStmt(Jimple.v().newVirtualInvokeExpr(localTracker, branchHit.makeRef(), IntConstant.v(defaultBranchId))));


			}

			Unit lastNop = Jimple.v().newNopStmt();

			newUnits.add(Jimple.v().newLookupSwitchStmt(key, lookupValues, targets, defaultTarget));
			processSwitch(newUnits, u, key, keys, keyTarget, keyBranchId, defaultTarget, defaultBranchId, lastNop);
			newUnits.add(lastNop);


		}
		public void process(Chain<Unit> newUnits, TableSwitchStmt u) {
			final soot.Value key = u.getKey();
			Value v1 = getValue(getRealUse(key, u, false));
			final DataUse use = handleUse(newUnits, key, u);
			condDataType = getType(key);
			int current= switchCaseNoGenerator++;
			ConditionSwitch c =new ConditionSwitch(current,v1,condDataType,use);
			start.addCondition(c);
			
			final int lIndex = u.getLowIndex();
			final int hIndex = u.getHighIndex();
			//
			Unit trgtStmt=u.getTarget(0);
			Unit defaultStmt= u.getDefaultTarget();
			int[] keys = new int[hIndex-lIndex+1];
			List<Unit> targets = new ArrayList<Unit>();
			SortedMap<Integer, Unit> keyTarget = new TreeMap<Integer, Unit>();
			Map<Integer, Integer> keyBranchId = new HashMap<Integer, Integer>();
			for(int idx = 0; idx <= hIndex-lIndex; idx++) {

				final int value = idx+lIndex;
				keys[idx] = value;
				int caseNo=addBranch( u.getTarget(idx));
//				if( u.getTarget(idx).equals(defaultStmt))continue;

			    keyBranchId.put(value, caseNo);
				c.addCase(caseNo,value);

				Unit target = Jimple.v().newNopStmt();
				targets.add(target);
				keyTarget.put(value, target);
				newUnits.add(Jimple.v().newInvokeStmt(Jimple.v().newVirtualInvokeExpr(localTracker, branchHit.makeRef(), IntConstant.v(caseNo))));
			}
			Unit defaultTarget = Jimple.v().newNopStmt();
			Integer defaultBranchId;
			{
				defaultBranchId=addBranch( u.getDefaultTarget());
				c.setDefaultCase(defaultBranchId);
				newUnits.add(Jimple.v().newInvokeStmt(Jimple.v().newVirtualInvokeExpr(localTracker, branchHit.makeRef(), IntConstant.v(defaultBranchId))));

			}
			Unit lastNop = Jimple.v().newNopStmt();

			newUnits.add(Jimple.v().newTableSwitchStmt(key, u.getLowIndex(), u.getHighIndex(), targets, defaultTarget));
			processSwitch(newUnits, u, key, keys, keyTarget, keyBranchId, defaultTarget, defaultBranchId, lastNop);
			newUnits.add(lastNop);
			
		}
		private void processSwitch(Chain<Unit> newUnits, Unit switchUnit, soot.Value keyValue, int[] keys, SortedMap<Integer, Unit> keyTarget, Map<Integer, Integer> keyBranchId, Unit defaultTarget, int defaultBranchId, Unit lastNop) {
			for(final int key : keys) {
				final Unit start = keyTarget.get(key);
				final int branchId = keyBranchId.get(key);

				newUnits.add(start);

				// track distance

				// [0..i..n-1] i-th target; [n] -> default target;
				IntConstant[] ctlookupValues = new IntConstant[keys.length + 1];
				Unit[] ctTargets = new Unit[keys.length + 1];
				for(int i = 0; i < keys.length; i++) {
					ctlookupValues[i] = IntConstant.v(keyBranchId.get(keys[i]));
					ctTargets[i] = Jimple.v().newNopStmt();
				}
				ctlookupValues[keys.length] =  IntConstant.v(defaultBranchId);
				ctTargets[keys.length] = Jimple.v().newNopStmt();
				
				newUnits.add(Jimple.v().newAssignStmt(localConditionTarget, Jimple.v().newVirtualInvokeExpr(localTracker, getConditionTargetId.makeRef(),ctlookupValues[0])));
				newUnits.add(Jimple.v().newLookupSwitchStmt(localConditionTarget, Arrays.asList(ctlookupValues), Arrays.asList(ctTargets), lastNop));

		
				for(int i = 0; i < keys.length; i++) {
					final int ctKey = keys[i];
					newUnits.add(ctTargets[i]);
					newUnits.add(Jimple.v().newAssignStmt(localConditionTarget, Jimple.v().newVirtualInvokeExpr(localTracker, getConditionTargetId.makeRef(),IntConstant.v(keyBranchId.get(ctKey)))));

					if(ctKey == key) // branch executed!
//						newUnits.add(Jimple.v().newInvokeStmt(Jimple.v().newVirtualInvokeExpr(localTracker, setConditionTargetDistance.makeRef(), DoubleConstant.v(-1.0), (keyDef == null ? NullConstant.v() : keyDef))));
								newUnits.add(Jimple.v().newInvokeStmt(Jimple.v().newVirtualInvokeExpr(localTracker, branchCovered.makeRef(), IntConstant.v(branchId))));
					else
						newUnits.add(Jimple.v().newInvokeStmt(Jimple.v().newVirtualInvokeExpr(localTracker, calculateConditionTargetDistance.makeRef(), Arrays.asList(DoubleConstant.v(ctKey),DoubleConstant.v(key) ))));

//						newUnits.add(Jimple.v().newInvokeStmt(Jimple.v().newVirtualInvokeExpr(localTracker, setConditionTargetDistance.makeRef(), DoubleConstant.v(Math.abs(ctKey - key)), (keyDef == null ? NullConstant.v() : keyDef))));
//					newUnits.add(Jimple.v().newGotoStmt(lastNop));
				}

				{ // handle default target
					newUnits.add(ctTargets[keys.length]);
//					newUnits.add(Jimple.v().newInvokeStmt(Jimple.v().newVirtualInvokeExpr(localTracker, calculateConditionTargetDistance.makeRef(), DoubleConstant.v(switchDistanceToDefault(key, keyBranchId.keySet())))));
					newUnits.add(Jimple.v().newAssignStmt(localConditionTarget, Jimple.v().newVirtualInvokeExpr(localTracker, getConditionTargetId.makeRef(),IntConstant.v(defaultBranchId))));

					newUnits.add(Jimple.v().newInvokeStmt(Jimple.v().newVirtualInvokeExpr(localTracker, calculateConditionTargetDistance.makeRef(), Arrays.asList(DoubleConstant.v(switchDistanceToDefault(key, keyBranchId.keySet())),DoubleConstant.v(0) ))));
					//
					newUnits.add(Jimple.v().newGotoStmt(lastNop));
				}
			}

			// manage default target
			{
				newUnits.add(defaultTarget);
				newUnits.add(Jimple.v().newInvokeStmt(Jimple.v().newVirtualInvokeExpr(localTracker, branchCovered.makeRef(), IntConstant.v(defaultBranchId))));
//				newUnits.add(Jimple.v().newInvokeStmt(Jimple.v().newVirtualInvokeExpr(localTracker, trackBranch.makeRef(), IntConstant.v(defaultBranchId))));


				// track distance
				IntConstant[] ctlookupValues = new IntConstant[keys.length + 1];
				Unit[] ctTargets = new Unit[keys.length + 1];
				for(int i = 0; i < keys.length; i++) {
					ctlookupValues[i] = IntConstant.v(keyBranchId.get(keys[i]));
					ctTargets[i] = Jimple.v().newNopStmt();
				}
				ctlookupValues[keys.length] =  IntConstant.v(defaultBranchId);
				ctTargets[keys.length] = Jimple.v().newNopStmt();
				newUnits.add(Jimple.v().newAssignStmt(localConditionTarget, Jimple.v().newVirtualInvokeExpr(localTracker, getConditionTargetId.makeRef(),ctlookupValues[0])));
				newUnits.add(Jimple.v().newLookupSwitchStmt(localConditionTarget, Arrays.asList(ctlookupValues), Arrays.asList(ctTargets), lastNop));

				for(int i = 0; i < keys.length; i++) {
					final int ctKey = keys[i];
					newUnits.add(ctTargets[i]);
					
					newUnits.add(Jimple.v().newAssignStmt(localConditionTarget, Jimple.v().newVirtualInvokeExpr(localTracker, getConditionTargetId.makeRef(),IntConstant.v(keyBranchId.get(keys[i])))));

					newUnits.add(Jimple.v().newAssignStmt(localTmpDouble1, Jimple.v().newCastExpr(keyValue, DoubleType.v())));
					newUnits.add(Jimple.v().newInvokeStmt(Jimple.v().newVirtualInvokeExpr(localTracker, calculateConditionTargetDistance.makeRef(), Arrays.asList(DoubleConstant.v(ctKey), localTmpDouble1))));
//					newUnits.add(Jimple.v().newGotoStmt(lastNop));
				}

				{ // handle default target
					newUnits.add(ctTargets[keys.length]);
					newUnits.add(Jimple.v().newInvokeStmt(Jimple.v().newVirtualInvokeExpr(localTracker, branchCovered.makeRef(), IntConstant.v(defaultBranchId))));
					newUnits.add(Jimple.v().newGotoStmt(lastNop));
				}
			}

		}
		private int  addBranch( Unit stmt) {
			int caseNo=branchId++;
				
			return caseNo;
		}
		private int switchDistanceToDefault(int key, Set<Integer> keySet) {
			if(!keySet.contains(key)) return 0;

			int d = 1;
			while(keySet.contains(key-d) && keySet.contains(key + d)) d++;

			return d;
		}


		private Data getData(soot.Value value) {

			if(value == null) return null;
			if(value instanceof Constant) return null;
			if(value instanceof Expr) return null;

			if(value instanceof Local) {
				Local local = (Local) value;
				Data ret = localRepository.get(local);
				if(ret == null) {
						ret = new Data(null,local.getType().toString(),null,-1, false);
					localRepository.put(local, ret);
				}
				return ret;
			}

			if(value instanceof FieldRef)
				return Factory.singleton.get(((FieldRef) value).getField());
			return null;
		}

		public Value getValue(soot.Value v) {
			
			if (v instanceof soot.jimple.Constant) {

				if(v instanceof NullConstant) return new ConstantString(null);
				if(v instanceof IntConstant) return new Constant(((IntConstant) v).value);
				if(v instanceof LongConstant) return new Constant(((LongConstant) v).value);
				if(v instanceof FloatConstant) return new Constant(((FloatConstant) v).value);
				if(v instanceof DoubleConstant) return new Constant(((DoubleConstant) v).value);
				if(v instanceof StringConstant) return new ConstantString(((StringConstant) v).value);;

				return null;
			}

			if(v instanceof Local || v instanceof FieldRef)
				return getData(v);

			if(v instanceof Expr) 
					return null;

			if(v instanceof ArrayRef) return null;

			return null;
		}		
		public void processPost(Chain<Unit> newUnits, Stmt stmt) {

		}		
		private soot.Value getRealUse(soot.Value v, Unit u, boolean skipTrivial) {
			if(v == null) return null;


			if(u == null) return v;

			if(v instanceof Local) {

				List<Unit> reachingDefs = duAnalysis.getDefsOfAt((Local) v, u);

				if(reachingDefs.size() < 1) {
					if(skipTrivial) return null;
					return v;
				}

				// if the use has only one reaching definition, I can skip its tracking or check if it is a temporary variable
				if(reachingDefs.size() == 1) {

					// check if it is a temporary variable used to access a field
					Unit def = reachingDefs.get(0);

					if(def instanceof AssignStmt) {
						AssignStmt a = (AssignStmt)def;
						soot.Value rightOp = a.getRightOp();
						if(rightOp instanceof soot.jimple.internal.JVirtualInvokeExpr ){
							VirtualInvokeExpr fr = (VirtualInvokeExpr) rightOp;
							if(!condDataType.equals(condDataType.Character))
								condDataType=getType(fr.getBase());
							if(condDataType.equals(condDataType.String)){
								return v;
							}
						}
							else{
								if((rightOp instanceof FieldRef)){// && duAnalysis.getUsesOf(def).size() == 1) {
		//							logger.info("Found temporary local " + v + " tracking uses of " + rightOp + " instead");
									return getRealUse(rightOp, def, skipTrivial);
									
		
								} else if(rightOp instanceof CastExpr) {
									soot.Value op = ((CastExpr) rightOp).getOp();
									logger.info("Found cast " + u + ": tracking uses of " + op + " instead");
									return getRealUse(op, def, skipTrivial);
		
								}else if( rightOp instanceof soot.jimple.internal.JInterfaceInvokeExpr){
											soot.jimple.internal.JInterfaceInvokeExpr fr = (soot.jimple.internal.JInterfaceInvokeExpr) rightOp;
											soot.Value op = fr.getBase();
											return getRealUse(op, def, skipTrivial);
									}else if( rightOp instanceof soot.jimple.internal.JStaticInvokeExpr){
										soot.jimple.internal.JStaticInvokeExpr fr = (soot.jimple.internal.JStaticInvokeExpr) rightOp;
										skipTrivial=false;//changed 3.2104 true ->false
										if(fr.getArgCount()>0)
											return getRealUse(fr.getArg(0), def, skipTrivial);
									}else if(rightOp instanceof soot.jimple.internal.JSpecialInvokeExpr){
										soot.jimple.internal.JSpecialInvokeExpr fr = (soot.jimple.internal.JSpecialInvokeExpr) rightOp;
										skipTrivial=false;//changed 3.2104 true ->false
										if(fr.getArgCount()>0)
											return getRealUse(fr.getArg(0), def, skipTrivial);

									}else if(rightOp instanceof soot.jimple.StringConstant){
										return getRealUse(rightOp, def, skipTrivial);
									}
							}
					}

					if(skipTrivial) {
//						logger.info("Skipping instrumentation of use of " + v + " in " + u + ": only 1 reachable def-use");
						return null;
					}

					return v;
				}
			}

			return v;
		}
		public void process(Chain<Unit> newUnits, ReturnStmt u) {
			defs.clear();
			methodCall = null;
		}

		public void process(Chain<Unit> newUnits, AssignStmt u) {
			
			handleDef(newUnits, u);			
		}
		private void handleDef(Chain<Unit> newUnits, DefinitionStmt stmt) {
			 DataDef dataDef=null;
			final soot.Value leftOp = stmt.getLeftOp();
			soot.Value rightOp = stmt.getRightOp();
			if(readOnlyParams.contains(getData(getRealUse(rightOp, stmt, false))))
				readOnlyParams.remove(getData(getRealUse(rightOp, stmt, false)));

			if(leftOp instanceof Local && !(leftOp.getType() instanceof ArrayType)) {
				 Value v=getValue(getRealUse(leftOp, stmt, false));
					if(rightOp instanceof Expr ){
						if(rightOp instanceof VirtualInvokeExpr ){
							VirtualInvokeExpr fr = (VirtualInvokeExpr) rightOp;
						
							Data base=getData(getRealUse(fr.getBase(), stmt, false));
								String methodRef=fr.getMethod().getName();
							condDataType=getType(fr.getBase());
							List <Value> args=new LinkedList<Value>();
							if(base==null){//(if "--".equal(str))
								if(getValue(getRealUse(fr.getBase(), stmt, false))instanceof ConstantString){
									args.add(getValue(getRealUse(fr.getBase(), stmt, false)));
									if(fr.getArgs().size()==1)
										base=getData(getRealUse(fr.getArg(0), stmt, false));
								}
							}
							else{
									for(int i=0; i<fr.getArgs().size();i++){
										Value arg=getValue(getRealUse(fr.getArg(i),stmt,false));
		//								if(arg instanceof Constant|| arg instanceof ConstantString)
											args.add(arg);
									}
							}
							Virtualinvoke virtualinvoke=new Virtualinvoke(base,methodRef,args);
								dataDef = new DataDef( (Data)v, virtualinvoke);
								  manageDefs(dataDef);
						}
						else  if( rightOp instanceof SpecialInvokeExpr){//if(rightOp instanceof VirtualInvokeExpr ){
							SpecialInvokeExpr fr = (SpecialInvokeExpr) rightOp;
						
							Data base=getData(getRealUse(fr.getBase(), stmt, false));
								String methodRef=fr.getMethod().getName();
							condDataType=getType(fr.getBase());
							List <Value> args=new LinkedList<Value>();
							if(base==null){//(if "--".equal(str))
								if(getValue(getRealUse(fr.getBase(), stmt, false))instanceof ConstantString){
									args.add(getValue(getRealUse(fr.getBase(), stmt, false)));
									if(fr.getArgs().size()==1)
										base=getData(getRealUse(fr.getArg(0), stmt, false));
								}
							}
							else{
									for(int i=0; i<fr.getArgs().size();i++){
										Value arg=getValue(getRealUse(fr.getArg(i),stmt,false));
		//								if(arg instanceof Constant|| arg instanceof ConstantString)
											args.add(arg);
									}
							}
							Virtualinvoke virtualinvoke=new Virtualinvoke(base,methodRef,args);
								dataDef = new DataDef( (Data)v, virtualinvoke);
								  manageDefs(dataDef);
						}
						else if(rightOp instanceof soot.jimple.internal.JInstanceOfExpr){
							soot.jimple.internal.JInstanceOfExpr fr= (soot.jimple.internal.JInstanceOfExpr) rightOp;
							soot.Value op1 = fr.getOp();
							Data base=getData(getRealUse(op1, stmt, false));
							String methodRef="instanceof";
							List <Value> args=new LinkedList<Value>();
							
							Value arg=new ConstantString(fr.getCheckType().toString());
							args.add(arg);
							Virtualinvoke virtualinvoke=new Virtualinvoke(base,methodRef,args);
								dataDef = new DataDef( (Data)v, virtualinvoke);
								  manageDefs(dataDef);

						} else 	if( rightOp instanceof soot.jimple.internal.JInterfaceInvokeExpr){
											soot.jimple.internal.JInterfaceInvokeExpr fr = (soot.jimple.internal.JInterfaceInvokeExpr) rightOp;
											Data base=getData(getRealUse(fr.getBase(), stmt, false)); //"r0.equal(r2) r0 is the base"
											String methodRef=fr.getMethod().getName();
											condDataType=getType(fr.getBase());
											List <Value> args=new LinkedList<Value>();
											for(int i=0; i<fr.getArgs().size();i++){
												Value arg=getValue(getRealUse(fr.getArg(i),stmt,false));
													args.add(arg);
											}
											Virtualinvoke virtualinvoke=new Virtualinvoke(base,methodRef,args);
											dataDef = new DataDef( (Data)v, virtualinvoke);
											  manageDefs(dataDef);


						} else 	if( rightOp instanceof soot.jimple.internal.JLengthExpr){
							soot.jimple.internal.JLengthExpr fr = (soot.jimple.internal.JLengthExpr) rightOp;
							soot.Value op1 = fr.getOp();
							String methodRef="Length";//fr.getType().toString();
							Data base=getData(getRealUse(op1, stmt, false));
							Value arg=null;
							ValueArray valueArray=new ValueArray(base,methodRef,arg);
							dataDef = new DataDef( (Data)v, valueArray);
							  manageDefs(dataDef);
			
						}else if(rightOp instanceof soot.jimple.internal.JStaticInvokeExpr){//r0.<shoppingcart.products.Product: double price>
 							soot.jimple.internal.JStaticInvokeExpr fr = (soot.jimple.internal.JStaticInvokeExpr) rightOp;
							for(int i=0;i<fr.getArgCount();i++){
								
								System.out.println(" soot.jimple.internal.JStaticInvokeExpr:" +fr.getArg(i));
								Value arg=getValue(getRealUse(fr.getArg(i),stmt,false));
								if(arg instanceof StringConstant)
									staticInvokeArgs.add(arg);
								else if( arg  instanceof Data){
									if(((Data) arg).isParam()||((Data) arg).isField())
										staticInvokeArgs.add(arg);
									else{
										for(DataDef def:defs){
											if( def.toString().contains(arg.toString())){
												System.out.println(def);
												Value right=def.getValue();
												System.out.println(right);
												if(right instanceof ConstantString)
													staticInvokeArgs.add(right);
											}
										}
									}
								}
							}
						}
						if(dataDef!=null)
							return;
					}else if(rightOp instanceof JArrayRef) {
						soot.jimple.internal.JArrayRef fr = (soot.jimple.internal.JArrayRef) rightOp;
						soot.Value op1 = fr.getBase();
						String methodRef="index";//fr.getType().toString();
						Data base=getData(getRealUse(op1, stmt, false));
						Value arg;
						if(fr.getIndexBox().getValue() instanceof IntConstant){
							soot.Value temp=fr.getIndexBox().getValue();
							 arg=getValue(temp);
						}
						else{
							 arg=getValue(getRealUse(fr.getIndexBox().getValue(), stmt, false));
						}
						ValueArray valueArray=new ValueArray(base,methodRef,arg);
						dataDef = new DataDef( (Data)v, valueArray);
						  manageDefs(dataDef);
						  return;
					}

			}else if(leftOp instanceof JArrayRef) {
				soot.jimple.internal.JArrayRef fr = (soot.jimple.internal.JArrayRef) leftOp;
				soot.Value op1 = fr.getBase();
				String methodRef="index";//fr.getType().toString();
				Data base=getData(getRealUse(op1, stmt, false));
				Value arg;
				if(fr.getIndexBox().getValue() instanceof IntConstant){
					soot.Value temp=fr.getIndexBox().getValue();
					 arg=getValue(temp);
				}
				else{
					 arg=getValue(getRealUse(fr.getIndexBox().getValue(), stmt, false));
				}
				ValueArray valueArray=new ValueArray(base,methodRef,arg);

				Value v=null;
				if(rightOp instanceof Local){
					v=getValue(getRealUse(rightOp, stmt, false));
				}if(rightOp instanceof StringConstant)
					v=getValue(rightOp);
				else if(getValue(rightOp) instanceof Constant){
					v=getValue(rightOp);
				}else if(rightOp instanceof JArrayRef){
					System.err.println("leftOp instanceof JArrayRef  && rightOp instanceof JArrayRef");
					System.exit(1);

				}
				
				dataDef = new DataDef(  (Value) valueArray,v);
				  manageDefs(dataDef);
				  return;
			}else if(leftOp.getType() instanceof ArrayType) { // Deals with array
					if(rightOp instanceof CastExpr){
						System.err.println("leftOp instanceof ArrayType && rightOp instanceof CastExpr");
						System.exit(1);
					}
					if(rightOp instanceof Local){
						//System.err.println("leftOp instanceof NewArrayExpr && rightOp instanceof Local");
						Data d=getData(leftOp);
						Value v=getValue(getRealUse(rightOp, stmt, false));
						dataDef = new DataDef( d, v);
					}else if(rightOp instanceof NewArrayExpr){
						//System.err.println("rightOp instanceof NewArrayExpr");
						NewArrayExpr fr = (NewArrayExpr ) rightOp;
						String methodRef="newArray";
						Value arg;
						if(fr.getSize() instanceof IntConstant)
							 arg= new Constant(((IntConstant) fr.getSize()).value);
					    else
					    	arg= getData(getRealUse(fr.getSize(), stmt, false));

						ValueArray valueArray=new ValueArray(null,methodRef,arg);
						Data d=getData(leftOp);
						dataDef = new DataDef( (Data)d, valueArray);
					}else if(rightOp instanceof JInstanceFieldRef||rightOp instanceof soot.jimple.StaticFieldRef) {
						 Value v=getValue(getRealUse(rightOp, stmt, false));
						 Data d=getData(leftOp);
						dataDef = new DataDef( d, v);

					}else if(rightOp instanceof soot.jimple.StaticFieldRef){
						System.err.println("leftOp instanceof ArrayType && rightOp instanceof CastExpr *** "+rightOp);
						System.exit(1);

					}else if(rightOp instanceof JVirtualInvokeExpr){
						Value v=getValue(getRealUse(leftOp, stmt, false));
						JVirtualInvokeExpr fr = (JVirtualInvokeExpr) rightOp;
						Data base=getData(getRealUse(fr.getBase(), stmt, false));
							String methodRef=fr.getMethod().getName();
						condDataType=getType(fr.getBase());
						List <Value> args=new LinkedList<Value>();
						if(base==null){//(if "--".equal(str))
							if(getValue(getRealUse(fr.getBase(), stmt, false))instanceof ConstantString){
								args.add(getValue(getRealUse(fr.getBase(), stmt, false)));
								if(fr.getArgs().size()==1)
									base=getData(getRealUse(fr.getArg(0), stmt, false));
							}
						}
						else{
								for(int i=0; i<fr.getArgs().size();i++){
									Value arg=getValue(getRealUse(fr.getArg(i),stmt,false));
	//								if(arg instanceof Constant|| arg instanceof ConstantString)
										args.add(arg);
								}
						}
						Virtualinvoke virtualinvoke=new Virtualinvoke(base,methodRef,args);
							dataDef = new DataDef( (Data)v, virtualinvoke);
					}
					if(dataDef!=null)	
						manageDefs(dataDef);

				return;
			}



				Data d=getData(leftOp);
				 Value v=getValue(getRealUse(rightOp, stmt, false));
				dataDef = new DataDef( d, v);
				manageDefs(dataDef);
		}
		private void manageDefs(DataDef def) {
			Value data =(Value) def.getData();

			Iterator<DataDef> iter = defs.iterator();
			while(iter.hasNext())
				if(iter.next().getData() == data)
					iter.remove();

			defs.add(def);

		}

		public void process(Chain<Unit> newUnits, DefinitionStmt stmt) {
			System.out.println();
		}



	}

	}
